import CustomValue2D from "./CustomValue2D";
import { MaxMultiTextures } from "./MainConfig";
import { MyAnimationCache } from "./MyAnimationCache";
import { MyAnimationShaderMeshQuadTexture } from "./MyAnimationShaderMeshQuadTexture";
import { MySubmitTarget } from "./MySubmitTarget";

export class MyShaderAnimation extends Laya.AnimationBase {
    protected meshName: string = "_myAnimationMesh";
    public _textureIdx = 0;
    anchorX = 0.5;
    anchorY = 0.5;

    set textureIdx(value) {
        this._textureIdx = value;
    }

    get textureIdx() {
        return this._textureIdx;
    }

    attributes = [
        ["posuv", Laya.glTFAccessorComponentType.FLOAT, 4],
        ["textureIdx", Laya.glTFAccessorComponentType.BYTE, 1],
    ];

    _attribLocation = [];
    verticeTypes = [];
    static vs: string = `
        attribute vec4 posuv;
        attribute float textureIdx;
        uniform vec2 size;	
        varying vec2 uv;
        varying float texIdx;
        void main() {
            vec4 pos = vec4(posuv.xy,0.,1.);
            vec4 pos1  =vec4((pos.x/size.x-0.5)*2.0,(0.5-pos.y/size.y)*2.0,0.,1.0);
            gl_Position=pos1;
            uv = posuv.zw;
            texIdx = textureIdx;
        }`;
    static ps: string = `#if defined(GL_FRAGMENT_PRECISION_HIGH)
        precision highp float;
        #else
        precision mediump float;
        #endif
        varying vec2 uv;
        varying float texIdx;
        uniform sampler2D texture0;
        uniform sampler2D texture1;
        uniform sampler2D texture2;
        uniform sampler2D texture3;
        uniform sampler2D texture4;
        uniform sampler2D texture5;
        uniform sampler2D texture6;
        uniform sampler2D texture7;
        uniform vec2 size;

        void main() {
            vec4 color = vec4(0.0, 0.0, 0.0, 0.0);
            if (texIdx < 0.5) {
                color= texture2D(texture0, uv);
            } else if (texIdx < 1.5) {
                color = texture2D(texture1, uv);
            } else if (texIdx < 2.5) {
                color = texture2D(texture2, uv);
            } else if (texIdx < 3.5) {
                color = texture2D(texture3, uv);
            } else if (texIdx < 4.5) {
                color = texture2D(texture4, uv);
            } else if (texIdx < 5.5) {
                color = texture2D(texture5, uv);
            } else if (texIdx < 6.5) {
                color = texture2D(texture6, uv);
            } else {
                color = texture2D(texture7, uv);
            }

            gl_FragColor=color;
        }`;
    constructor() {
        super();
        this.customRenderEnable = true;

        for (let i = 0; i < this.attributes.length; i++) {
            this._attribLocation.push(this.attributes[i][0], i);
        }
        for (let i = 0; i < this.attributes.length; i++) {
            let attr = this.attributes[i];
            for (let t = 0; t < attr[2]; t++) {
                this.verticeTypes.push(attr[1]);
            }
        }

        this['_setControlNode'](this);
    }
    private static _shadervalue = null;
    public static getShaderValue() {
        if (!this._shadervalue) {
            this._shadervalue = new CustomValue2D(MyShaderAnimation.vs, MyShaderAnimation.ps);
        }
        return this._shadervalue;
    }

    // 覆盖掉animation的_displayToIndex函数
    protected _displayToIndex(value: number): void {
    }

    private url = null;
    protected myAtlas = null;

    aniName = null;
    playAni(name, loop = true, cb?) {
        this.aniName = name;
        this._count = MyAnimationCache.cache[name].length;
        this.interval = MyAnimationCache.intervals[name] || this.interval;
        super.play(0, loop, name);

        if (cb) {
            this.once(Laya.Event.COMPLETE, null, cb);
        }
    }

    customRender(context: Laya.Context, x: number, y: number): void {
        if (!this.getCurFrame())
            return;

        if (!this.aniName)
            return;
        // 原本这里直接调用了drawTarget
        // 现在改成抄一边drawTarget里的代码，然后魔改
        context['_drawCount']++;
        // 自定义的mesh
        let mesh = context[this.meshName];
        if (!mesh || mesh.vertNum + 4 > Laya.Context['_MAXVERTNUM'] || mesh.released) {

            mesh = MyAnimationShaderMeshQuadTexture.getAMesh(this.meshName, this.attributes, context.isMain);
            context.meshlist.push(mesh);
            context[this.meshName] = mesh;
        }

        let tex = this.getCurFrame();
        let positions = [];
        let width = tex.width;
        let height = tex.height;
        context.transformQuad(x, y, width, height, 0, context['_curMat'], positions);


        // 判断shaervalue是否一致，这里换了submit后，就进入下一个drawcall了
        let sv = context['_curSubmit'].shaderValue;
        if (!this.checkShader(sv) || !this.checkTextures(sv)) {
            this.textureIdx = 0;
            let shaderValue = CustomValue2D.createOne(MyShaderAnimation.vs, MyShaderAnimation.ps, 111);
            shaderValue["atlasurl0"] = this.url;
            for (let i = 1; i < MaxMultiTextures; i++) {
                shaderValue['texture' + i] = null;
                shaderValue['atlasurl' + i] = null;
            }
            this.checkTextures(shaderValue);
            shaderValue.shader = Laya.Shader.sharders[1 | 111] || new Laya.Shader2X(MyShaderAnimation.vs, MyShaderAnimation.ps, 111, null, this._attribLocation);


            var submit = context['_curSubmit'] = MySubmitTarget.create(context, mesh, shaderValue);
            submit.blendType = context['_nBlendType'];
            context['_copyClipInfo'](submit, context['_globalClipMatrix']);
            submit['_numEle'] = 6;
            context['_submits'][context['_submits']._length++] = submit;
            context['_cubSubmit'] = Laya.SubmitBase.RENDERBASE;
        } else {
            context['_curSubmit']._numEle += 6;
        }
        this.addQuad(mesh, positions);
        mesh.indexNum += 6;
        mesh.vertNum += 4;
    }

    checkShader(sv) {
        if (sv.vs == MyShaderAnimation.vs && sv.ps == MyShaderAnimation.ps)
            return true;
        return false;
    }

    checkTextures(sv: CustomValue2D) {
        let curFrame = this.getCurFrame();
        let url = curFrame.bitmap.url;
        for (let i = 0; i < MaxMultiTextures; i++) {
            if (sv["atlasurl" + i] == url) {
                this.textureIdx = i;
                return true;
            }
            if (!sv["atlasurl" + i]) {
                sv["texture" + i] = curFrame._getSource();
                sv["atlasurl" + i] = url;
                this.textureIdx = i;
                return true;
            }
        }
        return false;
    }

    getCurFrame() {
        let idx = this.index || 0;
        if (MyAnimationCache.cache[this.aniName]) {
            let frame = MyAnimationCache.cache[this.aniName][idx];
            return frame;
        }
    }

    addQuad(mesh, pos) {
        var vb = mesh._vb;
        var vpos = (vb._byteLength >> 2);
        vb.setByteLength((vpos + mesh.const_stride) << 2);
        var vbdata = vb._floatArray32 || vb.getFloat32Array();
        var vbu32Arr = vb._uint32Array;
        var cpos = vpos;

        let vertices = this.getVertices(pos);
        let typeIdx = 0;
        for (let i = 0; i < vertices.length; i++) {
            if (this.verticeTypes[typeIdx] != Laya.glTFAccessorComponentType.FLOAT) {
                vbu32Arr[cpos++] = vertices[i];
            } else {
                vbdata[cpos++] = vertices[i];
            }
            typeIdx++;
            typeIdx = typeIdx % this.verticeTypes.length;
        }
        vb._upload = true;
    }

    getVertices(pos) {
        let tex = this.getCurFrame();
        let uv = tex['_uv'];
        let width = this.width || tex.width;
        let height = this.height || tex.height;

        let sourceWidth = tex.sourceWidth;
        let sourceHeight = tex.sourceHeight;

        let offsetX = (tex.offsetX - sourceWidth * this.anchorX) * this.scaleX;
        let offsetY = (tex.offsetY - sourceHeight * this.anchorY) * this.scaleY;

        width = width * this.scaleX;
        height = height * this.scaleY;
        let vertices = [];

        vertices.push(pos[0] + offsetX);
        vertices.push(pos[1] + offsetY);
        vertices.push(uv[0]);
        vertices.push(uv[1]);
        this.pushExt(vertices);

        vertices.push(pos[2] + offsetX);
        vertices.push(pos[3] + offsetY);
        vertices.push(uv[2]);
        vertices.push(uv[3]);
        this.pushExt(vertices);

        vertices.push(pos[4] + offsetX);
        vertices.push(pos[5] + offsetY);
        vertices.push(uv[4]);
        vertices.push(uv[5]);
        this.pushExt(vertices);

        vertices.push(pos[6] + offsetX);
        vertices.push(pos[7] + offsetY);
        vertices.push(uv[6]);
        vertices.push(uv[7]);
        this.pushExt(vertices);

        return vertices;
    }

    pushExt(vertices) {
        vertices.push(this._textureIdx);
    }
};